package top.tangyh.lamp.care.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.tangyh.basic.exception.BizException;
import top.tangyh.lamp.care.common.TimeTypeEnum;
import top.tangyh.lamp.care.entity.BWorkModules;
import top.tangyh.lamp.care.entity.BWorkPlan;
import top.tangyh.lamp.care.mapper.BCareRecordMapper;
import top.tangyh.lamp.care.mapper.BTimePlanMapper;
import top.tangyh.lamp.care.mapper.BWorkModulesMapper;
import top.tangyh.lamp.care.mapper.BWorkPlanMapper;
import top.tangyh.lamp.care.service.BCareRecordService;
import top.tangyh.lamp.care.service.BObsvRecordService;
import top.tangyh.lamp.care.service.BTimePlanService;
import top.tangyh.basic.base.service.impl.SuperServiceImpl;
import top.tangyh.lamp.care.manager.BTimePlanManager;
import top.tangyh.lamp.care.entity.BTimePlan;
import top.tangyh.lamp.care.service.BWorkPlanService;
import top.tangyh.lamp.care.vo.query.WorkPlanDetailQueryVO;
import top.tangyh.lamp.care.vo.result.TimePointOutVO;
import top.tangyh.lamp.care.vo.result.WorkPlanDetailOutVO;
import top.tangyh.lamp.care.vo.save.TimePlanInVO;
import top.tangyh.lamp.common.utils.IcuDateUtils;
import top.tangyh.lamp.common.utils.PDateUtils;
import top.tangyh.lamp.common.utils.YDateUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务实现类
 * 病人时间计划表
 * </p>
 *
 * @author zqb
 * @date 2024-07-18 15:15:54
 * @create [2024-07-18 15:15:54] [zqb] [代码生成器生成]
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class BTimePlanServiceImpl extends SuperServiceImpl<BTimePlanManager, Long, BTimePlan> implements BTimePlanService {

    private final BTimePlanMapper bTimePlanMapper;

    private final BWorkPlanMapper bWorkPlanMapper;

    private final BWorkPlanService bWorkPlanService;


    private final BObsvRecordService bObsvRecordService;


//    private final BCareRecordService bCareRecordService;

    private  final BCareRecordMapper bCareRecordMapper;
    private final BWorkModulesMapper bWorkModulesMapper;

    @Override
    public LinkedHashSet<TimePointOutVO> getAllTimePoitByPt(String moduleId, Long roleId, String patientIdent, String hospitalNumber, Long startTime, Long endTime) {
        LinkedHashSet<TimePointOutVO> rst = new LinkedHashSet<>();
        if (StrUtil.isBlank(moduleId) || startTime == null || endTime == null) {
            return rst;
        }
        Set<Long> timeBaseAxios = getTimePoitSet(moduleId, roleId, startTime, endTime);

        //病人自定义时间点
        List<BTimePlan> userTimePointList = new ArrayList<>();
        if (StrUtil.isNotBlank(hospitalNumber)) {
            userTimePointList = bTimePlanMapper.selectListByBusiField(moduleId, patientIdent, hospitalNumber);
        }

        for (BTimePlan o : userTimePointList) {
            if (o == null || o.getTimePoint() == null) {
                continue;
            }
            long time = PDateUtils.getTimestampOfDateTime(o.getTimePoint());
            if (time >= startTime && time <= endTime) {
                TimePointOutVO vo = new TimePointOutVO();
                vo.setTimePoint(time);
                vo.setId(o.getId());
                rst.add(vo);
            }
        }
        //班次时间点
        for (Long time : timeBaseAxios) {
            if (time == null) {
                continue;
            }
            ;
            if (time >= startTime && time <= endTime) {
                TimePointOutVO vo = new TimePointOutVO();
                vo.setTimePoint(time);
                rst.add(vo);
            }
        }
        rst = rst.stream().sorted(Comparator.comparing(TimePointOutVO::getTimePoint)).collect(Collectors.toCollection(LinkedHashSet::new));
        return rst;

    }

    @Override
    public Set<Long> getTimePoitSet(String moduleId, Long roleId, Long startTime, Long endTime, Long currTime) {
        WorkPlanDetailQueryVO queryInVO = new WorkPlanDetailQueryVO();
        queryInVO.setModuleId(moduleId);
        queryInVO.setCurrentDate(DateUtil.beginOfDay(new Date(currTime)).getTime());
        queryInVO.setRoleId(roleId);
        List<WorkPlanDetailOutVO> list = bWorkPlanService.selectWorkPlanDetailListByQuery(queryInVO);
        if (list == null) {
            list = new ArrayList<>();
        }
        List<Long> rstList = new ArrayList<Long>();
        for (WorkPlanDetailOutVO detail : list) {
            if (detail == null || detail.getTimePointList() == null) {
                continue;
            }
            for (TimePointOutVO timePoit : detail.getTimePointList()) {
                if (timePoit == null || timePoit.getTimePoint() == null) {
                    continue;
                }
                rstList.add(timePoit.getTimePoint());
            }
        }
        rstList.stream().filter(o -> o >= startTime && o <= endTime).sorted();
        return new LinkedHashSet<>(rstList);
    }


    @Override
    public Set<Long> getTimePoitSet(String moduleId, Long roleId, Long startTime, Long endTime) {
        WorkPlanDetailQueryVO queryInVO = new WorkPlanDetailQueryVO();
        queryInVO.setModuleId(moduleId);
        queryInVO.setCurrentDate(DateUtil.beginOfDay(new Date(startTime)).getTime());
        queryInVO.setRoleId(roleId);
        List<WorkPlanDetailOutVO> list = bWorkPlanService.selectWorkPlanDetailListByQuery(queryInVO);
        if (list == null) {
            list = new ArrayList<>();
        }
        List<Long> rstList = new ArrayList<Long>();
        for (WorkPlanDetailOutVO detail : list) {
            if (detail == null || detail.getTimePointList() == null) {
                continue;
            }
            for (TimePointOutVO timePoit : detail.getTimePointList()) {
                if (timePoit == null || timePoit.getTimePoint() == null) {
                    continue;
                }
                rstList.add(timePoit.getTimePoint());
            }
        }
        rstList.stream().filter(o -> o >= startTime && o <= endTime).sorted();
        return new LinkedHashSet<>(rstList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTimePlan(Long id) {
        BTimePlan yxTimePlanPO = bTimePlanMapper.selectById(id);
        bTimePlanMapper.deleteById(id);

        if(null!=yxTimePlanPO){
            if (yxTimePlanPO.getModuleId().equals("gcx")) {
                bObsvRecordService.deleteData(yxTimePlanPO.getPatientIdent(), yxTimePlanPO.getHospitalNumber(), yxTimePlanPO.getTimePoint());
            }
            if (yxTimePlanPO.getModuleId().equals("hlcz")) {
                bCareRecordMapper.delCellByTimePiont(yxTimePlanPO.getPatientIdent(), yxTimePlanPO.getHospitalNumber(), yxTimePlanPO.getTimePoint());
            }
        }

        return false;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> add(TimePlanInVO inVO) {
        String type = inVO.getType();
        TimeTypeEnum timeTypeEnum = TimeTypeEnum.forValue(type);
        if (timeTypeEnum == null) {
            throw new BizException("输入时间类型不对");
        }
        //精确到天的时间戳
        Long currentDay = inVO.getCurrentDate();
        if (null != inVO.getAddDay() && inVO.getAddDay() != 0) {
            currentDay = YDateUtils.addDateDays(new Date(inVO.getCurrentDate()), inVO.getAddDay()).getTime();
        }
        String startTime = inVO.getStartTime();
        int startHour = IcuDateUtils.getHour(startTime);
        int startMinute = IcuDateUtils.getMinute(startTime);
        //开始的时间戳
        Long startTimestamp = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, startHour, startMinute);
        //返回新增的时间点
        List<Long> newAddTimePoints = new ArrayList<>();

        //1.时间点处理
        if (timeTypeEnum.equals(TimeTypeEnum.TIME_POINT)) {
            //判断时间点是否重复，重复的不添加
            Set<Long> workPlanTimePoints = getMergeTimePoints(currentDay, inVO.getModuleId(), inVO.getHospitalNumber());
            //不包含的情况下才去增加 病人的时间计划
            if (!workPlanTimePoints.contains(startTimestamp)) {
                newAddTimePoints.add(startTimestamp);
                addTimePlan(inVO.getPatientIdent(), inVO.getHospitalNumber(), inVO.getModuleId(), startTimestamp);

//                if(StringUtil.isNotEmpty(inVO.getCaijiren())){
//                    CaijirenConfigPO caijirenConfigPO=new CaijirenConfigPO();
//                    caijirenConfigPO.setTime(new Date(startTimestamp));
//                    caijirenConfigPO.setName(inVO.getCaijiren());
//                    caijirenConfigPO.setTimeType(1);
//                    caijirenConfigPO.setPatientIdent(inVO.getPatientIdent());
//                    caijirenConfigPO.setOperatorType(1);
//                    caijirenConfigService.save(caijirenConfigPO);
//
//                }
                return newAddTimePoints;
            }


        }

        //2.时间段处理
        if (timeTypeEnum.equals(TimeTypeEnum.TIME_RANGE)) {

            String endTime = inVO.getEndTime();
            int endHour = IcuDateUtils.getHour(endTime);
            int endMinute = IcuDateUtils.getMinute(endTime);
            Long endTimestamp = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, endHour, endMinute);
            //说明添加的时间段是跨天了 需要+1
            if (endHour < startHour) {
                endTimestamp = IcuDateUtils.getTimestampByHourMinute(currentDay, 1, endHour, endMinute);
            }

            int totalMinutes = IcuDateUtils.getTotalMinutesByHourMinute(startHour, startMinute, endHour, endMinute);
            Integer interval = inVO.getInterval();
            int counts = IcuDateUtils.getIntervalCounts(totalMinutes, interval);
            //获取设置病人的时间点集合
            Set<Long> patTimePoints = IcuDateUtils.getAllTimePointsByTimeRange(startTimestamp, endTimestamp, counts, interval, null);

            //根据模块查询出模块的时间轴，判断时间点是否重复，重复的不添加
            Set<Long> workPlanTimePoints = getMergeTimePoints(currentDay, inVO.getModuleId(), inVO.getHospitalNumber());
            patTimePoints.forEach(timePoint -> {
                //没有重复的情况下才添加属于病人的时间点
                if (!workPlanTimePoints.contains(timePoint)) {
                    newAddTimePoints.add(timePoint);
                    addTimePlan(inVO.getPatientIdent(), inVO.getHospitalNumber(), inVO.getModuleId(), timePoint);
                }
            });

//            if (StringUtil.isNotEmpty(inVO.getCaijiren())) {
//                CaijirenConfigPO caijirenConfigPO = new CaijirenConfigPO();
//                caijirenConfigPO.setStartTime(new Date(startTimestamp));
//                caijirenConfigPO.setEndTime(new Date(endTimestamp));
//                caijirenConfigPO.setName(inVO.getCaijiren());
//                caijirenConfigPO.setTimeType(2);
//                caijirenConfigPO.setPatientIdent(inVO.getPatientIdent());
//                caijirenConfigPO.setOperatorType(1);
//                caijirenConfigService.save(caijirenConfigPO);
//
//                //如果这段时间内有观察项记录 就把设备采集的采集人名称改成指定名称
//                LambdaUpdateWrapper<YxObsvRecordEntity> wrapper = Wrappers.<YxObsvRecordEntity>lambdaUpdate()
//                        .set(YxObsvRecordEntity::getCaijiren, inVO.getCaijiren())
//                        .set(YxObsvRecordEntity::getCaijirenGh, inVO.getGh())
//
//                        .set(YxObsvRecordEntity::getCreateBy, inVO.getCaijiren())
//                        .set(YxObsvRecordEntity::getUpdateBy, inVO.getCaijiren())
//
//                        .ge(YxObsvRecordEntity::getRecTime, startTimestamp)
//                        .le(YxObsvRecordEntity::getRecTime, endTimestamp)
//                        .eq(YxObsvRecordEntity::getPatientIdent, inVO.getPatientIdent())
//                        .eq(YxObsvRecordEntity::getCreateSource, "dev");
//
//                Integer result = yxObsvRecordEntityMapper.update(null, wrapper);
//            }
            return newAddTimePoints;
        }

        return newAddTimePoints;
    }


    /**
     * 合并排班时间点和病人设置的时间计划点，总的一个不重复的时间点集合
     *
     * @param currentDay     精确到天的时间戳
     * @param moduleId       模块id
     * @param hospitalNumber 住院号
     * @return
     */
    public Set<Long> getMergeTimePoints(Long currentDay, String moduleId, String hospitalNumber) {
        //根据模块查找所有的时间点timestamp
        Set<Long> workTimePoints = getWorkPlanTimePointsByModule(moduleId, currentDay);
        if (CollectionUtil.isEmpty(workTimePoints)) {
            throw new BizException("当前设置的模块没有找到对应的排班配置,请检查配置");
        }
        //获取患者的时间计划
        Set<Long> patTimePoints = getPatTimePointsByCurrentDay(currentDay, moduleId, hospitalNumber);
        workTimePoints.addAll(patTimePoints);
        //返回合并后的timestamp集合
        return workTimePoints;
    }

    /**
     * 根据当天日期、模块id、住院号查找病人的所有时间点timestamp
     *
     * @param currentDay     精确到天的时间戳
     * @param moduleId       模块id
     * @param hospitalNumber 住院号
     * @return
     */
    public Set<Long> getPatTimePointsByCurrentDay(Long currentDay, String moduleId, String hospitalNumber) {
        Long dayStartTime = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, 0, 0);
        String startDate = DateUtil.formatDateTime(DateUtil.date(dayStartTime));
        Long dayEndTime = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, 23, 59);
        String endDate = DateUtil.formatDateTime(DateUtil.date(dayEndTime));
        //根据病人住院号 模块 当天的开始0-23：59范围的所有的添加过的时间点
        Set<Long> patTimePointsByTimeRange = getPatTimePointsByTimeRange(startDate, endDate, Collections.singletonList(moduleId), hospitalNumber);
        return patTimePointsByTimeRange;
    }


    /**
     * 根据时间范围 模块id、住院号查找病人的所有时间点timestamp
     *
     * @param startDate      开始日期 例如: 2021:11:01 00:00:00
     * @param endDate        结束日期 例如: 2021:11:01 23:59:00
     * @param moduleIdList   模块id集合
     * @param hospitalNumber 住院号
     * @return
     */
    public Set<Long> getPatTimePointsByTimeRange(String startDate, String endDate, List<String> moduleIdList, String hospitalNumber) {
        List<BTimePlan> patTimePlanList = bTimePlanMapper.selectListByRangeAndModuleId(startDate, endDate, moduleIdList, hospitalNumber);
        Set<Long> patTimePointsByDay = patTimePlanList.stream().map(timePlanPO -> PDateUtils.getTimestampOfDateTime(timePlanPO.getTimePoint())).collect(Collectors.toSet());
        return patTimePointsByDay;
    }


    /**
     * 根据模块获取所关联的所有排班计划时间轴集合 未排序的
     * 一个模块会有多个排班 A P N班次
     * 这里返回的是每个班次的时间点，整合一起后不重复的时间点集合，班次有交叉部分也只返回一次timestamp
     *
     * @param moduleId   模块id
     * @param currentDay 精确到天的时间戳
     * @return
     */
    public Set<Long> getWorkPlanTimePointsByModule(String moduleId, long currentDay) {
        Set<Long> workIdSet = bWorkModulesMapper.selectListByModuleId(moduleId).stream().map(BWorkModules::getWorkId).collect(Collectors.toSet());
        if (CollectionUtil.isNotEmpty(workIdSet)) {
            //包含所有排班计划的时间点集合
            Set<Long> timePointsSet = new HashSet<>();

            List<BWorkPlan> workPlanPOList = bWorkPlanMapper.selectListByIds(workIdSet);
            //遍历模块下每个排班计划
            workPlanPOList.forEach(yxWorkPlanPO -> {
                String startTime = yxWorkPlanPO.getStartTime();
                int startHour = IcuDateUtils.getHour(startTime);
                int startMinute = IcuDateUtils.getMinute(startTime);
                String endTime = yxWorkPlanPO.getEndTime();
                int endHour = IcuDateUtils.getHour(endTime);
                int endMinute = IcuDateUtils.getMinute(endTime);
                Integer interval = yxWorkPlanPO.getInterval();
                int totalMinutes = IcuDateUtils.getTotalMinutesByHourMinute(startHour, startMinute, endHour, endMinute);
                //总分钟除以间隔 算出有几个
                int counts = getIntervalCounts(totalMinutes, interval);
                long startTimestamp = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, startHour, startMinute);
                long endTimestamp = IcuDateUtils.getTimestampByHourMinute(currentDay, 0, endHour, endMinute);
                Set<Long> allTimePointsByTimeRange = getAllTimePointsByTimeRange(startTimestamp, endTimestamp, counts, interval, moduleId);
                timePointsSet.addAll(allTimePointsByTimeRange);
            });
            return timePointsSet;
        }
        return null;
    }

    /**
     * 根据开始和结束时间戳范围、间隔次数、间隔，获取具体时间点集合
     *
     * @param startTimestamp 开始时间戳
     * @param endTimestamp   结束时间戳
     * @param counts         间隔次数
     * @param interval       间隔分钟数
     * @return
     */
    public static Set<Long> getAllTimePointsByTimeRange(long startTimestamp, long endTimestamp, int counts, int interval,String moduleId) {
        Set<Long> timePointsSet = new HashSet<>();
        for (int i = 1; i <= counts; i++) {
            //依次添加到时间集合中
            long timePoint = startTimestamp + getMinuteMillisecond(i * interval);
            timePointsSet.add(timePoint);
        }
        if(StrUtil.isNotEmpty(moduleId) && moduleId.equals("crl")){
            timePointsSet.add(endTimestamp);
            timePointsSet.add(endTimestamp+getMinuteMillisecond(interval));
        }
        else{
            timePointsSet.add(startTimestamp);
            timePointsSet.add(endTimestamp);
        }
        return timePointsSet;
    }


    /**
     * 获取分钟的毫秒数
     *
     * @param minute 分钟数
     * @return
     */
    public static long getMinuteMillisecond(int minute) {
        return (long) minute * 60 * 1000;
    }

    /**
     * 根据总分钟数和间隔分钟获取间隔次数
     *
     * @param totalMinutes
     * @param interval
     * @return
     */
    public static int getIntervalCounts(int totalMinutes, int interval) {
        return totalMinutes / interval;
    }

    /**
     * 插入时间计划方法
     *
     * @param patientIdent
     * @param hospitalNumber
     * @param moduleId
     * @param timePoint
     */
    @Transactional(rollbackFor = Exception.class)
    public void addTimePlan(String patientIdent, String hospitalNumber, String moduleId, Long timePoint) {
        BTimePlan yxTimePlanPO = new BTimePlan();
        yxTimePlanPO.setPatientIdent(patientIdent);
        yxTimePlanPO.setHospitalNumber(hospitalNumber);
        yxTimePlanPO.setTimePoint(PDateUtils.getDateTimeOfTimestamp(timePoint));
        yxTimePlanPO.setModuleId(moduleId);
        bTimePlanMapper.insert(yxTimePlanPO);
    }



}


